package com.xyh.transaction.filter;

import com.alibaba.fastjson.JSON;
import com.xyh.transaction.common.Constant;
import com.xyh.transaction.config.IgnoreUrlsConfig;
import com.xyh.transaction.entity.LoginUser;
import com.xyh.transaction.exception.TokenValidationException;
import com.xyh.transaction.response.ResponseCode;
import com.xyh.transaction.response.ResponseResult;
import com.xyh.transaction.utils.JwtUtil;
import com.xyh.transaction.utils.RedisUtil;
import com.xyh.transaction.utils.WebUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.TimeUnit;
@Slf4j
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private JwtUtil jwtUtil;

    @Value("${jwt.header}")
    private String tokenHeader;

    @Autowired
    private IgnoreUrlsConfig ignoreUrlsConfig;

    private final AntPathMatcher pathMatcher = new AntPathMatcher();

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {

        String requestURI = request.getRequestURI();

        // 路径放行逻辑
        if (isPathAllowed(requestURI)) {
            filterChain.doFilter(request, response);
            return;
        }

        // 获取 Token
        String token = request.getHeader(tokenHeader);

        // 针对游客访问
        if (isAnonymousAllowed(requestURI) && token == null) {
            filterChain.doFilter(request, response);
            return;
        }

        if (token == null) {
            sendUnauthorizedResponse(response, Constant.EMPTY_TOKEN_DETAIL);
            return;
        }

        // 校验 Token 并处理结果
        Map<String, String> tokenData = jwtUtil.decode(token);
        if (tokenData == null) {
            sendUnauthorizedResponse(response, Constant.ILLEGAL_TOKEN_DETAIL);
            return;
        }

        String userId = tokenData.get("userId");
        String username = tokenData.get("username");
        String redisKey = String.format("login_%s_%s", userId, username);
        LoginUser loginUser = redisUtil.get(redisKey, LoginUser.class);

        if (loginUser == null) {
            sendUnauthorizedResponse(response, Constant.EXITED_DETAIL);
            return;
        }

        // Token 过期处理
        if (jwtUtil.isTokenExpired(token)) {
            refreshToken(response, loginUser, redisKey);
            return;
        }

        // 用户认证
        authenticateUser(loginUser);

        filterChain.doFilter(request, response);
    }

    /**
     * 判断路径是否在允许访问的范围内
     */
    private boolean isPathAllowed(String path) {
        List<String> allowedPaths = new ArrayList<>(ignoreUrlsConfig.getPermitAll());
        allowedPaths.addAll(ignoreUrlsConfig.getAnonymous());
        return allowedPaths.stream().anyMatch(p -> pathMatcher.match(p, path));
    }

    /**
     * 判断路径是否允许匿名访问
     */
    private boolean isAnonymousAllowed(String path) {
        return ignoreUrlsConfig.getSpecial().stream().anyMatch(p -> pathMatcher.match(p, path));
    }

    /**
     * 用户认证
     */
    private void authenticateUser(LoginUser loginUser) {
        UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
                loginUser, null, loginUser.getAuthorities());
        SecurityContextHolder.getContext().setAuthentication(authentication);
    }

    /**
     * 刷新 Token 并返回给客户端
     */
    private void refreshToken(HttpServletResponse response, LoginUser loginUser, String redisKey) throws IOException {
        String newToken = jwtUtil.generateJwtToken(loginUser.getUser().getUsername(),
                loginUser.getUser().getId().toString());
        redisUtil.set(redisKey, loginUser, Constant.JWT_EXPIRE_TIME * 2, TimeUnit.MILLISECONDS);

        ResponseResult<String> result = new ResponseResult<>(ResponseCode.EXPIRE_TOKEN, newToken);
        WebUtil.renderString(response, JSON.toJSONString(result));

        authenticateUser(loginUser);
        log.info("用户：{} 的 Token 已刷新", loginUser.getUser().getUsername());
    }

    /**
     * 返回未授权的错误响应
     */
    private void sendUnauthorizedResponse(HttpServletResponse response, String message) throws IOException {
        WebUtil.renderString(response, JSON.toJSONString(
                ResponseResult.failure(HttpStatus.UNAUTHORIZED.value(), message)));
    }
}
